package net.mycrub.bitwise.core.impl;

import net.mycrub.bitwise.core.ILayer;
import net.mycrub.bitwise.core.IRetroActionCallback;
import net.mycrub.bitwise.core.util.Random;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root;

@Root
public class LowCutLayer extends AbstractLayer implements ILayer {

    @Attribute
    private int size;
    @Attribute
    private double min;
    @Attribute
    private double max;
    @Attribute
    private double cut;
    @Attribute
    private double mutator;
    
    public LowCutLayer(
            @Attribute(name="size") int size, 
            @Attribute(name="min") double min, 
            @Attribute(name="max") double max, 
            @Attribute(name="cut") double cut, 
            @Attribute(name="mutator") double mutator) {
        this.size = size;
        this.min = min;
        this.max = max;
        this.cut = cut;
        this.mutator = mutator;
    }

    public LowCutLayer(int size, double min, double max, double mutator) {
        this(size, min, max, (min + max) / 2., mutator);
    }

    public ILayer combine(ILayer partner) {
        if (!LowCutLayer.class.isAssignableFrom(partner.getClass())) {
            throw new IllegalArgumentException(
                    "Layers are not compatible for combination "
                            + this.getClass().getName() + "/"
                            + partner.getClass().getName());
        }
        
        LowCutLayer lPart = (LowCutLayer) partner;
        
        return new LowCutLayer(
                (this.size + lPart.size) / 2, 
                (this.min + lPart.min) / 2, 
                (this.max + lPart.max) / 2, 
                (this.cut + lPart.cut) / 2);
    }

    public ILayer duplicate() {
        return new LowCutLayer(size, min, max, cut);
    }

    public int getInputSize() {
        return size;
    }

    public int getOutputSize() {
        return size;
    }

    public void mutate() {
        min = min * mutationFactor();
        max = max * mutationFactor();
        cut = cut * mutationFactor();
    }
    
    private double mutationFactor() {
        return 1 + (Random.next() - .5) * mutator ;
    }
    

    public double[] process(double[] input, IRetroActionCallback callback) {
        double inputMin = 0;
        double inputMax = 0;

        for (int i = 0; i < input.length; i++) {
            if (i == 0 || input[i] < inputMin) {
                inputMin = input[i];
            }
            if (i == 0 || input[i] > inputMax) {
                inputMax = input[i];
            }
        }

        double inputCut = inputMin + (cut - min) / (max - min) * (inputMax - inputMin) ;

        double[] output = new double[input.length];
        for (int i = 0; i < input.length; i++) {
            output[i] = input[i] <= inputCut ? min : max;
        }
        return output;
    }

}
